package com.wlt.util;

import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;

/**
 * 分布式可重入锁工具类（封装Lua脚本的执行逻辑）
 */
@Component
public class RedissonReentrantLockUtil {
    // 注入Redis模板（用于执行Lua脚本）
    private final StringRedisTemplate stringRedisTemplate;
    
    // 存储获取锁的Lua脚本（从文件加载或直接定义）
    private final DefaultRedisScript<Long> acquireLockScript;
    
    // 存储释放锁的Lua脚本
    private final DefaultRedisScript<Long> releaseLockScript;
    
    // 线程局部变量：存储当前线程的唯一标识（避免每次传递）
    private static final ThreadLocal<String> THREAD_ID = ThreadLocal.withInitial(
        () -> UUID.randomUUID().toString()
    );
    
    // 构造方法：初始化脚本和Redis模板
    public RedissonReentrantLockUtil(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
        
        // 初始化获取锁脚本
        acquireLockScript = new DefaultRedisScript<>();
        acquireLockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/acquire_lock.lua")));
        acquireLockScript.setResultType(Long.class);
        
        // 初始化释放锁脚本
        releaseLockScript = new DefaultRedisScript<>();
        releaseLockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/release_lock.lua")));
        releaseLockScript.setResultType(Long.class);
    }
    
    /**
     * 获取分布式可重入锁
     * @param lockKey 锁的名称（如"lock:order:1001"）
     * @param expireTime 过期时间（毫秒）
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, long expireTime) {
        String threadId = THREAD_ID.get();          // 获取当前线程唯一标识
        // 执行获取锁的Lua脚本
        Long result = stringRedisTemplate.execute(
            acquireLockScript,
            Collections.singletonList(lockKey),     // KEYS参数
            String.valueOf(expireTime), threadId    // ARGV参数
        );
        // 脚本返回1表示获取/重入成功，0表示失败
        return result != null && result == 1;
    }
    
    /**
     * 释放分布式可重入锁
     * @param lockKey 锁的名称
     * @return 是否释放成功
     */
    public boolean unlock(String lockKey) {
        String threadId = THREAD_ID.get();
        // 执行释放锁的Lua脚本
        Long result = stringRedisTemplate.execute(
            releaseLockScript,
            Collections.singletonList(lockKey),
            null, threadId                  // 注意：释放锁时ARGV[1]（过期时间）可传null（脚本中实际用不到）
        );
        // 脚本返回1表示释放成功，0表示非法释放
        return result != null && result == 1;
    }
    
    /**
     * 清除当前线程的唯一标识（如线程池场景下避免内存泄漏）
     */
    public void clearThreadId() {
        THREAD_ID.remove();
    }
}
